"""
任务调度器
支持定时任务和周期性任务的调度
"""

from loguru import logger
from datetime import datetime, timezone
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.date import DateTrigger
from apscheduler.triggers.interval import IntervalTrigger

from app.models.task.task_data import Task, TaskType
from app.core.sse_manager import sse_manager


class TaskScheduler:
    """任务调度器 - 支持定时和周期性任务"""

    def __init__(self):
        self.scheduler = AsyncIOScheduler()
        self.scheduled_jobs: dict[str, str] = {}  # task_id -> job_id
        self._is_running = False

    def start(self):
        """启动调度器"""
        if not self._is_running:
            self.scheduler.start()
            self._is_running = True
            logger.info("任务调度器已启动")

    def stop(self):
        """停止调度器"""
        if self._is_running:
            try:
                logger.info("正在停止任务调度器...")
                # 设置等待时间避免无限等待
                self.scheduler.shutdown(wait=False)
                self._is_running = False
                logger.info("任务调度器已停止")
            except Exception as e:
                logger.error(f"停止调度器时出错: {e}")
                self._is_running = False

    async def schedule_task(self, task: Task, task_manager) -> bool:
        """调度任务"""
        try:
            if task.type == TaskType.SCHEDULED:
                return await self._schedule_one_time_task(task, task_manager)
            elif task.type == TaskType.PERIODIC:
                return await self._schedule_periodic_task(task, task_manager)
            else:
                logger.error(f"不支持的调度任务类型: {task.type}")
                return False
        except Exception as e:
            logger.error(f"调度任务失败: {task.name}, 错误: {str(e)}")
            return False

    async def _schedule_one_time_task(self, task: Task, task_manager) -> bool:
        """调度一次性定时任务"""
        if not task.schedule_time:
            logger.error(f"定时任务 {task.name} 缺少调度时间")
            return False

        # 规范化时间对象，确保两个datetime对象具有相同的时区信息
        now = datetime.now()
        schedule_time = task.schedule_time

        # 如果schedule_time带时区而now不带，则将now转换为相同时区
        if schedule_time.tzinfo is not None and now.tzinfo is None:
            now = now.replace(tzinfo=timezone.utc)
        # 如果schedule_time不带时区而now带时区，则移除now的时区
        elif schedule_time.tzinfo is None and now.tzinfo is not None:
            now = now.replace(tzinfo=None)
        # 如果schedule_time不带时区，将其转换为naive datetime
        elif schedule_time.tzinfo is not None:
            schedule_time = schedule_time.replace(tzinfo=None)

        # 检查调度时间是否已过
        if schedule_time <= now:
            logger.warning(f"定时任务 {task.name} 调度时间已过，立即执行")
            # 立即执行
            await task_manager.execute_task(task)
            return True

        # 创建定时任务
        job = self.scheduler.add_job(
            func=self._execute_scheduled_task,
            trigger=DateTrigger(run_date=task.schedule_time),
            args=[task, task_manager],
            id=f"scheduled_{task.id}",
            name=f"Scheduled: {task.name}",
            replace_existing=True,
        )

        self.scheduled_jobs[task.id] = job.id
        logger.info(f"定时任务已调度: {task.name}, 执行时间: {task.schedule_time}")

        # 发送调度通知
        await sse_manager.send_system_message(
            f"定时任务已调度: {task.name}，将在 {task.schedule_time.strftime('%Y-%m-%d %H:%M:%S')} 执行"
        )

        return True

    async def _schedule_periodic_task(self, task: Task, task_manager) -> bool:
        """调度周期性任务"""
        if not task.interval or task.interval <= 0:
            logger.error(f"周期性任务 {task.name} 缺少有效的间隔时间")
            return False

        # 创建周期性任务
        job = self.scheduler.add_job(
            func=self._execute_scheduled_task,
            trigger=IntervalTrigger(seconds=task.interval),
            args=[task, task_manager],
            id=f"periodic_{task.id}",
            name=f"Periodic: {task.name}",
            replace_existing=True,
        )

        self.scheduled_jobs[task.id] = job.id
        logger.info(f"周期性任务已调度: {task.name}, 间隔: {task.interval}秒")

        # 发送调度通知
        await sse_manager.send_system_message(
            f"周期性任务已调度: {task.name}，每 {task.interval} 秒执行一次"
        )

        return True

    async def _execute_scheduled_task(self, task: Task, task_manager):
        """执行调度的任务"""
        try:
            logger.info(f"开始执行调度任务: {task.name}")

            # 创建新的任务实例（保留原始配置）
            new_task = Task(
                name=f"{task.name}_exec_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
                type=(
                    TaskType.LIGHT
                    if task.type == TaskType.SCHEDULED
                    else TaskType.LIGHT
                ),
                func_name=task.func_name,
                args=task.args,
                kwargs=task.kwargs,
                priority=task.priority,
                timeout=task.timeout,
                max_retries=task.max_retries,
            )

            # 执行任务
            await task_manager.execute_task(new_task)

            # 如果是一次性任务，从调度中移除
            if task.type == TaskType.SCHEDULED:
                await self.cancel_task(task.id)

        except Exception as e:
            logger.error(f"调度任务执行失败: {task.name}, 错误: {str(e)}")
            await sse_manager.send_system_message(
                f"调度任务执行失败: {task.name}, 错误: {str(e)}", level="error"
            )

    async def cancel_task(self, task_id: str) -> bool:
        """取消调度任务"""
        if task_id not in self.scheduled_jobs:
            return False

        job_id = self.scheduled_jobs[task_id]
        try:
            self.scheduler.remove_job(job_id)
            del self.scheduled_jobs[task_id]
            logger.info(f"已取消调度任务: {task_id}")
            return True
        except Exception as e:
            logger.error(f"取消调度任务失败: {task_id}, 错误: {str(e)}")
            return False

    def get_scheduled_tasks(self) -> dict[str, dict]:
        """获取所有调度任务信息"""
        scheduled_info = {}
        for task_id, job_id in self.scheduled_jobs.items():
            try:
                job = self.scheduler.get_job(job_id)
                if job:
                    scheduled_info[task_id] = {
                        "job_id": job_id,
                        "name": job.name,
                        "next_run_time": (
                            job.next_run_time.isoformat() if job.next_run_time else None
                        ),
                        "trigger": str(job.trigger),
                    }
            except Exception as e:
                logger.error(f"获取调度任务信息失败: {task_id}, 错误: {str(e)}")

        return scheduled_info

    def is_running(self) -> bool:
        """检查调度器是否运行中"""
        return self._is_running and self.scheduler.running


# 全局调度器实例
task_scheduler = TaskScheduler()
